import * as echarts from '@/subpages/components/echarts/lib/echarts';
import * as numberUtil from '@/subpages/components/echarts/lib/util/number';
import LiquidShape from './liquidFillShape';

var parsePercent = numberUtil.parsePercent;

function isPathSymbol(symbol) {
	return symbol && symbol.indexOf('path://') === 0;
}

echarts.extendChartView({

	type: 'liquidFill',

	render: function (seriesModel, ecModel, api) {
		var self = this;
		var group = this.group;
		group.removeAll();

		var data = seriesModel.getData();

		var itemModel = data.getItemModel(0);

		var center = itemModel.get('center');
		var radius = itemModel.get('radius');

		var width = api.getWidth();
		var height = api.getHeight();
		var size = Math.min(width, height);
		// itemStyle
		var outlineDistance = 0;
		var outlineBorderWidth = 0;
		var showOutline = seriesModel.get('outline.show');

		if (showOutline) {
			outlineDistance = seriesModel.get('outline.borderDistance');
			outlineBorderWidth = parsePercent(
				seriesModel.get('outline.itemStyle.borderWidth'), size
			);
		}

		var cx = parsePercent(center[0], width);
		var cy = parsePercent(center[1], height);

		var outterRadius;
		var innerRadius;
		var paddingRadius;

		var isFillContainer = false;

		var symbol = seriesModel.get('shape');
		if (symbol === 'container') {
			// a shape that fully fills the container
			isFillContainer = true;

			outterRadius = [
				width / 2,
				height / 2
			];
			innerRadius = [
				outterRadius[0] - outlineBorderWidth / 2,
				outterRadius[1] - outlineBorderWidth / 2
			];
			paddingRadius = [
				parsePercent(outlineDistance, width),
				parsePercent(outlineDistance, height)
			];

			radius = [
				Math.max(innerRadius[0] - paddingRadius[0], 0),
				Math.max(innerRadius[1] - paddingRadius[1], 0)
			];
		} else {
			outterRadius = parsePercent(radius, size) / 2;
			innerRadius = outterRadius - outlineBorderWidth / 2;
			paddingRadius = parsePercent(outlineDistance, size);

			radius = Math.max(innerRadius - paddingRadius, 0);
		}

		if (showOutline) {
			var outline = getOutline();
			outline.style.lineWidth = outlineBorderWidth;
			group.add(getOutline());
		}

		var left = isFillContainer ? 0 : cx - radius;
		var top = isFillContainer ? 0 : cy - radius;

		var wavePath = null;

		group.add(getBackground());

		// each data item for a wave
		var oldData = this._data;
		var waves = [];
		data.diff(oldData)
			.add(function (idx) {
				var wave = getWave(idx, false);

				var waterLevel = wave.shape.waterLevel;
				wave.shape.waterLevel = isFillContainer ? height / 2 : radius;
				echarts.graphic.initProps(wave, {
					shape: {
						waterLevel: waterLevel
					}
				}, seriesModel);

				wave.z2 = 2;
				setWaveAnimation(idx, wave, null);

				group.add(wave);
				data.setItemGraphicEl(idx, wave);
				waves.push(wave);
			})
			.update(function (newIdx, oldIdx) {
				var waveElement = oldData.getItemGraphicEl(oldIdx);

				// new wave is used to calculate position, but not added
				var newWave = getWave(newIdx, false, waveElement);

				// changes with animation
				var shape = {};
				var shapeAttrs = ['amplitude', 'cx', 'cy', 'phase', 'radius', 'radiusY', 'waterLevel', 'waveLength'];
				for (var i = 0; i < shapeAttrs.length; ++i) {
					var attr = shapeAttrs[i];
					if (newWave.shape.hasOwnProperty(attr)) {
						shape[attr] = newWave.shape[attr];
					}
				}

				var style = {};
				var styleAttrs = ['fill', 'opacity', 'shadowBlur', 'shadowColor'];
				for (var i = 0; i < styleAttrs.length; ++i) {
					var attr = styleAttrs[i];
					if (newWave.style.hasOwnProperty(attr)) {
						style[attr] = newWave.style[attr];
					}
				}

				if (isFillContainer) {
					shape.radiusY = height / 2;
				}

				// changes with animation
				echarts.graphic.updateProps(waveElement, {
					shape: shape,
					x: newWave.x,
					y: newWave.y
				}, seriesModel);

				if (seriesModel.isUniversalTransitionEnabled && seriesModel.isUniversalTransitionEnabled()) {
					echarts.graphic.updateProps(waveElement, {
						style: style
					}, seriesModel);
				} else {
					waveElement.useStyle(style);
				}

				// instant changes
				var oldWaveClipPath = waveElement.getClipPath();
				var newWaveClipPath = newWave.getClipPath();

				waveElement.setClipPath(newWave.getClipPath());
				waveElement.shape.inverse = newWave.inverse;

				if (oldWaveClipPath && newWaveClipPath
                    && self._shape === symbol
                    // TODO use zrender morphing to apply complex symbol animation.
                    && !isPathSymbol(symbol)
				) {
					// Can be animated.
					echarts.graphic.updateProps(newWaveClipPath, {
						shape: oldWaveClipPath.shape
					}, seriesModel, { isFrom: true });
				}

				setWaveAnimation(newIdx, waveElement, waveElement);
				group.add(waveElement);
				data.setItemGraphicEl(newIdx, waveElement);
				waves.push(waveElement);
			})
			.remove(function (idx) {
				var wave = oldData.getItemGraphicEl(idx);
				group.remove(wave);
			})
			.execute();

		if (itemModel.get('label.show')) {
			group.add(getText(waves));
		}

		this._shape = symbol;
		this._data = data;

		/**
         * Get path for outline, background and clipping
         *
         * @param {number} r outter radius of shape
         * @param {boolean|undefined} isForClipping if the shape is used
         *                                          for clipping
         */
		function getPath(r, isForClipping) {
			if (symbol) {
				// customed symbol path
				if (isPathSymbol(symbol)) {
					var path = echarts.graphic.makePath(symbol.slice(7), {});
					var bouding = path.getBoundingRect();
					var w = bouding.width;
					var h = bouding.height;
					if (w > h) {
						h = r * 2 / w * h;
						w = r * 2;
					} else {
						w = r * 2 / h * w;
						h = r * 2;
					}

					var left = isForClipping ? 0 : cx - w / 2;
					var top = isForClipping ? 0 : cy - h / 2;
					path = echarts.graphic.makePath(
						symbol.slice(7),
						{},
						new echarts.graphic.BoundingRect(left, top, w, h)
					);
					if (isForClipping) {
						path.x = -w / 2;
						path.y = -h / 2;
					}
					return path;
				} else if (isFillContainer) {
					// fully fill the container
					var x = isForClipping ? -r[0] : cx - r[0];
					var y = isForClipping ? -r[1] : cy - r[1];
					return echarts.helper.createSymbol(
						'rect', x, y, r[0] * 2, r[1] * 2
					);
				} else {
					var x = isForClipping ? -r : cx - r;
					var y = isForClipping ? -r : cy - r;
					if (symbol === 'pin') {
						y += r;
					} else if (symbol === 'arrow') {
						y -= r;
					}
					return echarts.helper.createSymbol(symbol, x, y, r * 2, r * 2);
				}
			}

			return new echarts.graphic.Circle({
				shape: {
					cx: isForClipping ? 0 : cx,
					cy: isForClipping ? 0 : cy,
					r: r
				}
			});
		}
		/**
         * Create outline
         */
		function getOutline() {
			var outlinePath = getPath(outterRadius);
			outlinePath.style.fill = null;

			outlinePath.setStyle(seriesModel.getModel('outline.itemStyle')
				.getItemStyle());

			return outlinePath;
		}

		/**
         * Create background
         */
		function getBackground() {
			// Seperate stroke and fill, so we can use stroke to cover the alias of clipping.
			var strokePath = getPath(radius);
			strokePath.setStyle(seriesModel.getModel('backgroundStyle')
				.getItemStyle());
			strokePath.style.fill = null;

			// Stroke is front of wave
			strokePath.z2 = 5;

			var fillPath = getPath(radius);
			fillPath.setStyle(seriesModel.getModel('backgroundStyle')
				.getItemStyle());
			fillPath.style.stroke = null;

			var group = new echarts.graphic.Group();
			group.add(strokePath);
			group.add(fillPath);

			return group;
		}

		/**
         * wave shape
         */
		function getWave(idx, isInverse, oldWave) {
			var radiusX = isFillContainer ? radius[0] : radius;
			var radiusY = isFillContainer ? height / 2 : radius;

			var itemModel = data.getItemModel(idx);
			var itemStyleModel = itemModel.getModel('itemStyle');
			var phase = itemModel.get('phase');
			var amplitude = parsePercent(itemModel.get('amplitude'),
				radiusY * 2);
			var waveLength = parsePercent(itemModel.get('waveLength'),
				radiusX * 2);

			var value = data.get('value', idx);
			var waterLevel = radiusY - value * radiusY * 2;
			phase = oldWave ? oldWave.shape.phase
				: (phase === 'auto' ? idx * Math.PI / 4 : phase);
			var normalStyle = itemStyleModel.getItemStyle();
			if (!normalStyle.fill) {
				var seriesColor = seriesModel.get('color');
				var id = idx % seriesColor.length;
				normalStyle.fill = seriesColor[id];
			}

			var x = radiusX * 2;
			var wave = new LiquidShape({
				shape: {
					waveLength: waveLength,
					radius: radiusX,
					radiusY: radiusY,
					cx: x,
					cy: 0,
					waterLevel: waterLevel,
					amplitude: amplitude,
					phase: phase,
					inverse: isInverse
				},
				style: normalStyle,
				x: cx,
				y: cy,
			});
			wave.shape._waterLevel = waterLevel;

			var hoverStyle = itemModel.getModel('emphasis.itemStyle')
				.getItemStyle();
			hoverStyle.lineWidth = 0;

			wave.ensureState('emphasis').style = hoverStyle;
			echarts.helper.enableHoverEmphasis(wave);

			// clip out the part outside the circle
			var clip = getPath(radius, true);
			// set fill for clipPath, otherwise it will not trigger hover event
			clip.setStyle({
				fill: 'white'
			});
			wave.setClipPath(clip);

			return wave;
		}

		function setWaveAnimation(idx, wave, oldWave) {
			var itemModel = data.getItemModel(idx);

			var maxSpeed = itemModel.get('period');
			var direction = itemModel.get('direction');

			var value = data.get('value', idx);

			var phase = itemModel.get('phase');
			phase = oldWave ? oldWave.shape.phase
				: (phase === 'auto' ? idx * Math.PI / 4 : phase);

			var defaultSpeed = function (maxSpeed) {
				var cnt = data.count();
				return cnt === 0 ? maxSpeed : maxSpeed *
                    (0.2 + (cnt - idx) / cnt * 0.8);
			};
			var speed = 0;
			if (maxSpeed === 'auto') {
				speed = defaultSpeed(5000);
			} else {
				speed = typeof maxSpeed === 'function'
					? maxSpeed(value, idx) : maxSpeed;
			}

			// phase for moving left/right
			var phaseOffset = 0;
			if (direction === 'right' || direction == null) {
				phaseOffset = Math.PI;
			} else if (direction === 'left') {
				phaseOffset = -Math.PI;
			} else if (direction === 'none') {
				phaseOffset = 0;
			} else {
				console.error('Illegal direction value for liquid fill.');
			}

			// wave animation of moving left/right
			if (direction !== 'none' && itemModel.get('waveAnimation')) {
				wave
					.animate('shape', true)
					.when(0, {
						phase: phase
					})
					.when(speed / 2, {
						phase: phaseOffset + phase
					})
					.when(speed, {
						phase: phaseOffset * 2 + phase
					})
					.during(function () {
						if (wavePath) {
							wavePath.dirty(true);
						}
					})
					.start();
			}
		}

		/**
         * text on wave
         */
		function getText(waves) {
			var labelModel = itemModel.getModel('label');

			function formatLabel() {
				var formatted = seriesModel.getFormattedLabel(0, 'normal');
				var defaultVal = (data.get('value', 0) * 100);
				var defaultLabel = data.getName(0) || seriesModel.name;
				if (!isNaN(defaultVal)) {
					defaultLabel = defaultVal.toFixed(0) + '%';
				}
				return formatted == null ? defaultLabel : formatted;
			}

			var textRectOption = {
				z2: 10,
				shape: {
					x: left,
					y: top,
					width: (isFillContainer ? radius[0] : radius) * 2,
					height: (isFillContainer ? radius[1] : radius) * 2
				},
				style: {
					fill: 'transparent'
				},
				textConfig: {
					position: labelModel.get('position') || 'inside'
				},
				silent: true
			};
			var textOption = {
				style: {
					text: formatLabel(),
					textAlign: labelModel.get('align'),
					textVerticalAlign: labelModel.get('baseline')
				}
			};
			Object.assign(textOption.style, echarts.helper.createTextStyle(labelModel));

			var outsideTextRect = new echarts.graphic.Rect(textRectOption);
			var insideTextRect = new echarts.graphic.Rect(textRectOption);
			insideTextRect.disableLabelAnimation = true;
			outsideTextRect.disableLabelAnimation = true;

			var outsideText = new echarts.graphic.Text(textOption);
			var insideText = new echarts.graphic.Text(textOption);
			outsideTextRect.setTextContent(outsideText);

			insideTextRect.setTextContent(insideText);
			var insColor = labelModel.get('insideColor');
			insideText.style.fill = insColor;

			var group = new echarts.graphic.Group();
			group.add(outsideTextRect);
			group.add(insideTextRect);

			// clip out waves for insideText
			var boundingCircle = getPath(radius, true);

			wavePath = new echarts.graphic.CompoundPath({
				shape: {
					paths: waves
				},
				x: cx,
				y: cy
			});

			wavePath.setClipPath(boundingCircle);
			insideTextRect.setClipPath(wavePath);

			return group;
		}
	},

	dispose: function () {
		// dispose nothing here
	}
});
